1   /*
2    * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package com.sun.media.sound;
26  
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Comparator;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  /**
35   * This class decodes information from ModelPeformer for use in SoftVoice.
36   * It also adds default connections if they where missing in ModelPerformer.
37   *
38   * @author Karl Helgason
39   */
40  public class SoftPerformer {
41  
42      static ModelConnectionBlock[] defaultconnections
43              = new ModelConnectionBlock[42];
44  
45      static {
46          int o = 0;
47          defaultconnections[o++] = new ModelConnectionBlock(
48              new ModelSource(
49                  new ModelIdentifier("noteon", "on", 0),
50                  ModelStandardTransform.DIRECTION_MIN2MAX,
51                  ModelStandardTransform.POLARITY_UNIPOLAR,
52                  ModelStandardTransform.TRANSFORM_LINEAR),
53              1, new ModelDestination(new ModelIdentifier("eg", "on", 0)));
54  
55          defaultconnections[o++] = new ModelConnectionBlock(
56              new ModelSource(
57                  new ModelIdentifier("noteon", "on", 0),
58                  ModelStandardTransform.DIRECTION_MIN2MAX,
59                  ModelStandardTransform.POLARITY_UNIPOLAR,
60                  ModelStandardTransform.TRANSFORM_LINEAR),
61              1, new ModelDestination(new ModelIdentifier("eg", "on", 1)));
62  
63          defaultconnections[o++] = new ModelConnectionBlock(
64              new ModelSource(
65                  new ModelIdentifier("eg", "active", 0),
66                  ModelStandardTransform.DIRECTION_MIN2MAX,
67                  ModelStandardTransform.POLARITY_UNIPOLAR,
68                  ModelStandardTransform.TRANSFORM_LINEAR),
69              1, new ModelDestination(new ModelIdentifier("mixer", "active", 0)));
70  
71          defaultconnections[o++] = new ModelConnectionBlock(
72              new ModelSource(
73                  new ModelIdentifier("eg", 0),
74                  ModelStandardTransform.DIRECTION_MAX2MIN,
75                  ModelStandardTransform.POLARITY_UNIPOLAR,
76                  ModelStandardTransform.TRANSFORM_LINEAR),
77              -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
78  
79          defaultconnections[o++] = new ModelConnectionBlock(
80              new ModelSource(
81                  new ModelIdentifier("noteon", "velocity"),
82                  ModelStandardTransform.DIRECTION_MAX2MIN,
83                  ModelStandardTransform.POLARITY_UNIPOLAR,
84                  ModelStandardTransform.TRANSFORM_CONCAVE),
85              -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
86  
87          defaultconnections[o++] = new ModelConnectionBlock(
88              new ModelSource(
89                  new ModelIdentifier("midi", "pitch"),
90                  ModelStandardTransform.DIRECTION_MIN2MAX,
91                  ModelStandardTransform.POLARITY_BIPOLAR,
92                  ModelStandardTransform.TRANSFORM_LINEAR),
93              new ModelSource(new ModelIdentifier("midi_rpn", "0"),
94                  new ModelTransform() {
95                      public double transform(double value) {
96                          int v = (int) (value * 16384.0);
97                          int msb = v >> 7;
98                          int lsb = v & 127;
99                          return msb * 100 + lsb;
100                     }
101                 }),
102             new ModelDestination(new ModelIdentifier("osc", "pitch")));
103 
104         defaultconnections[o++] = new ModelConnectionBlock(
105             new ModelSource(
106                 new ModelIdentifier("noteon", "keynumber"),
107                 ModelStandardTransform.DIRECTION_MIN2MAX,
108                 ModelStandardTransform.POLARITY_UNIPOLAR,
109                 ModelStandardTransform.TRANSFORM_LINEAR),
110             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
111 
112         defaultconnections[o++] = new ModelConnectionBlock(
113             new ModelSource(
114                 new ModelIdentifier("midi_cc", "7"),
115                 ModelStandardTransform.DIRECTION_MAX2MIN,
116                 ModelStandardTransform.POLARITY_UNIPOLAR,
117                 ModelStandardTransform.TRANSFORM_CONCAVE),
118             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
119 
120         defaultconnections[o++] = new ModelConnectionBlock(
121             new ModelSource(
122                 new ModelIdentifier("midi_cc", "8"),
123                 ModelStandardTransform.DIRECTION_MIN2MAX,
124                 ModelStandardTransform.POLARITY_UNIPOLAR,
125                 ModelStandardTransform.TRANSFORM_LINEAR),
126             1000, new ModelDestination(new ModelIdentifier("mixer", "balance")));
127 
128         defaultconnections[o++] = new ModelConnectionBlock(
129             new ModelSource(
130                 new ModelIdentifier("midi_cc", "10"),
131                 ModelStandardTransform.DIRECTION_MIN2MAX,
132                 ModelStandardTransform.POLARITY_UNIPOLAR,
133                 ModelStandardTransform.TRANSFORM_LINEAR),
134             1000, new ModelDestination(new ModelIdentifier("mixer", "pan")));
135 
136         defaultconnections[o++] = new ModelConnectionBlock(
137             new ModelSource(
138                 new ModelIdentifier("midi_cc", "11"),
139                 ModelStandardTransform.DIRECTION_MAX2MIN,
140                 ModelStandardTransform.POLARITY_UNIPOLAR,
141                 ModelStandardTransform.TRANSFORM_CONCAVE),
142             -960, new ModelDestination(new ModelIdentifier("mixer", "gain")));
143 
144         defaultconnections[o++] = new ModelConnectionBlock(
145             new ModelSource(
146                 new ModelIdentifier("midi_cc", "91"),
147                 ModelStandardTransform.DIRECTION_MIN2MAX,
148                 ModelStandardTransform.POLARITY_UNIPOLAR,
149                 ModelStandardTransform.TRANSFORM_LINEAR),
150             1000, new ModelDestination(new ModelIdentifier("mixer", "reverb")));
151 
152         defaultconnections[o++] = new ModelConnectionBlock(
153             new ModelSource(
154                 new ModelIdentifier("midi_cc", "93"),
155                 ModelStandardTransform.DIRECTION_MIN2MAX,
156                 ModelStandardTransform.POLARITY_UNIPOLAR,
157                 ModelStandardTransform.TRANSFORM_LINEAR),
158             1000, new ModelDestination(new ModelIdentifier("mixer", "chorus")));
159 
160         defaultconnections[o++] = new ModelConnectionBlock(
161             new ModelSource(
162                 new ModelIdentifier("midi_cc", "71"),
163                 ModelStandardTransform.DIRECTION_MIN2MAX,
164                 ModelStandardTransform.POLARITY_BIPOLAR,
165                 ModelStandardTransform.TRANSFORM_LINEAR),
166             200, new ModelDestination(new ModelIdentifier("filter", "q")));
167         defaultconnections[o++] = new ModelConnectionBlock(
168             new ModelSource(
169                 new ModelIdentifier("midi_cc", "74"),
170                 ModelStandardTransform.DIRECTION_MIN2MAX,
171                 ModelStandardTransform.POLARITY_BIPOLAR,
172                 ModelStandardTransform.TRANSFORM_LINEAR),
173             9600, new ModelDestination(new ModelIdentifier("filter", "freq")));
174 
175         defaultconnections[o++] = new ModelConnectionBlock(
176             new ModelSource(
177                 new ModelIdentifier("midi_cc", "72"),
178                 ModelStandardTransform.DIRECTION_MIN2MAX,
179                 ModelStandardTransform.POLARITY_BIPOLAR,
180                 ModelStandardTransform.TRANSFORM_LINEAR),
181             6000, new ModelDestination(new ModelIdentifier("eg", "release2")));
182 
183         defaultconnections[o++] = new ModelConnectionBlock(
184             new ModelSource(
185                 new ModelIdentifier("midi_cc", "73"),
186                 ModelStandardTransform.DIRECTION_MIN2MAX,
187                 ModelStandardTransform.POLARITY_BIPOLAR,
188                 ModelStandardTransform.TRANSFORM_LINEAR),
189             2000, new ModelDestination(new ModelIdentifier("eg", "attack2")));
190 
191         defaultconnections[o++] = new ModelConnectionBlock(
192             new ModelSource(
193                 new ModelIdentifier("midi_cc", "75"),
194                 ModelStandardTransform.DIRECTION_MIN2MAX,
195                 ModelStandardTransform.POLARITY_BIPOLAR,
196                 ModelStandardTransform.TRANSFORM_LINEAR),
197             6000, new ModelDestination(new ModelIdentifier("eg", "decay2")));
198 
199         defaultconnections[o++] = new ModelConnectionBlock(
200             new ModelSource(
201                 new ModelIdentifier("midi_cc", "67"),
202                 ModelStandardTransform.DIRECTION_MIN2MAX,
203                 ModelStandardTransform.POLARITY_UNIPOLAR,
204                 ModelStandardTransform.TRANSFORM_SWITCH),
205             -50, new ModelDestination(ModelDestination.DESTINATION_GAIN));
206 
207         defaultconnections[o++] = new ModelConnectionBlock(
208             new ModelSource(
209                 new ModelIdentifier("midi_cc", "67"),
210                 ModelStandardTransform.DIRECTION_MIN2MAX,
211                 ModelStandardTransform.POLARITY_UNIPOLAR,
212                 ModelStandardTransform.TRANSFORM_SWITCH),
213             -2400, new ModelDestination(ModelDestination.DESTINATION_FILTER_FREQ));
214 
215         defaultconnections[o++] = new ModelConnectionBlock(
216             new ModelSource(
217                 new ModelIdentifier("midi_rpn", "1"),
218                 ModelStandardTransform.DIRECTION_MIN2MAX,
219                 ModelStandardTransform.POLARITY_BIPOLAR,
220                 ModelStandardTransform.TRANSFORM_LINEAR),
221             100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
222 
223         defaultconnections[o++] = new ModelConnectionBlock(
224             new ModelSource(
225                 new ModelIdentifier("midi_rpn", "2"),
226                 ModelStandardTransform.DIRECTION_MIN2MAX,
227                 ModelStandardTransform.POLARITY_BIPOLAR,
228                 ModelStandardTransform.TRANSFORM_LINEAR),
229             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
230 
231         defaultconnections[o++] = new ModelConnectionBlock(
232             new ModelSource(
233                 new ModelIdentifier("master", "fine_tuning"),
234                 ModelStandardTransform.DIRECTION_MIN2MAX,
235                 ModelStandardTransform.POLARITY_BIPOLAR,
236                 ModelStandardTransform.TRANSFORM_LINEAR),
237             100, new ModelDestination(new ModelIdentifier("osc", "pitch")));
238 
239         defaultconnections[o++] = new ModelConnectionBlock(
240             new ModelSource(
241                 new ModelIdentifier("master", "coarse_tuning"),
242                 ModelStandardTransform.DIRECTION_MIN2MAX,
243                 ModelStandardTransform.POLARITY_BIPOLAR,
244                 ModelStandardTransform.TRANSFORM_LINEAR),
245             12800, new ModelDestination(new ModelIdentifier("osc", "pitch")));
246 
247         defaultconnections[o++] = new ModelConnectionBlock(13500,
248                 new ModelDestination(new ModelIdentifier("filter", "freq", 0)));
249 
250         defaultconnections[o++] = new ModelConnectionBlock(
251                 Float.NEGATIVE_INFINITY, new ModelDestination(
252                 new ModelIdentifier("eg", "delay", 0)));
253         defaultconnections[o++] = new ModelConnectionBlock(
254                 Float.NEGATIVE_INFINITY, new ModelDestination(
255                 new ModelIdentifier("eg", "attack", 0)));
256         defaultconnections[o++] = new ModelConnectionBlock(
257                 Float.NEGATIVE_INFINITY, new ModelDestination(
258                 new ModelIdentifier("eg", "hold", 0)));
259         defaultconnections[o++] = new ModelConnectionBlock(
260                 Float.NEGATIVE_INFINITY, new ModelDestination(
261                 new ModelIdentifier("eg", "decay", 0)));
262         defaultconnections[o++] = new ModelConnectionBlock(1000,
263                 new ModelDestination(new ModelIdentifier("eg", "sustain", 0)));
264         defaultconnections[o++] = new ModelConnectionBlock(
265                 Float.NEGATIVE_INFINITY, new ModelDestination(
266                 new ModelIdentifier("eg", "release", 0)));
267         defaultconnections[o++] = new ModelConnectionBlock(1200.0
268                 * Math.log(0.015) / Math.log(2), new ModelDestination(
269                 new ModelIdentifier("eg", "shutdown", 0))); // 15 msec default
270 
271         defaultconnections[o++] = new ModelConnectionBlock(
272                 Float.NEGATIVE_INFINITY, new ModelDestination(
273                 new ModelIdentifier("eg", "delay", 1)));
274         defaultconnections[o++] = new ModelConnectionBlock(
275                 Float.NEGATIVE_INFINITY, new ModelDestination(
276                 new ModelIdentifier("eg", "attack", 1)));
277         defaultconnections[o++] = new ModelConnectionBlock(
278                 Float.NEGATIVE_INFINITY, new ModelDestination(
279                 new ModelIdentifier("eg", "hold", 1)));
280         defaultconnections[o++] = new ModelConnectionBlock(
281                 Float.NEGATIVE_INFINITY, new ModelDestination(
282                 new ModelIdentifier("eg", "decay", 1)));
283         defaultconnections[o++] = new ModelConnectionBlock(1000,
284                 new ModelDestination(new ModelIdentifier("eg", "sustain", 1)));
285         defaultconnections[o++] = new ModelConnectionBlock(
286                 Float.NEGATIVE_INFINITY, new ModelDestination(
287                 new ModelIdentifier("eg", "release", 1)));
288 
289         defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
290                 new ModelDestination(new ModelIdentifier("lfo", "freq", 0)));
291         defaultconnections[o++] = new ModelConnectionBlock(
292                 Float.NEGATIVE_INFINITY, new ModelDestination(
293                 new ModelIdentifier("lfo", "delay", 0)));
294         defaultconnections[o++] = new ModelConnectionBlock(-8.51318,
295                 new ModelDestination(new ModelIdentifier("lfo", "freq", 1)));
296         defaultconnections[o++] = new ModelConnectionBlock(
297                 Float.NEGATIVE_INFINITY, new ModelDestination(
298                 new ModelIdentifier("lfo", "delay", 1)));
299 
300     }
301     public int keyFrom = 0;
302     public int keyTo = 127;
303     public int velFrom = 0;
304     public int velTo = 127;
305     public int exclusiveClass = 0;
306     public boolean selfNonExclusive = false;
307     public boolean forcedVelocity = false;
308     public boolean forcedKeynumber = false;
309     public ModelPerformer performer;
310     public ModelConnectionBlock[] connections;
311     public ModelOscillator[] oscillators;
312     public Map<Integer, int[]> midi_rpn_connections = new HashMap<Integer, int[]>();
313     public Map<Integer, int[]> midi_nrpn_connections = new HashMap<Integer, int[]>();
314     public int[][] midi_ctrl_connections;
315     public int[][] midi_connections;
316     public int[] ctrl_connections;
317     private List<Integer> ctrl_connections_list = new ArrayList<Integer>();
318 
319     private static class KeySortComparator implements Comparator<ModelSource> {
320 
321         public int compare(ModelSource o1, ModelSource o2) {
322             return o1.getIdentifier().toString().compareTo(
323                     o2.getIdentifier().toString());
324         }
325     }
326     private static KeySortComparator keySortComparator = new KeySortComparator();
327 
328     private String extractKeys(ModelConnectionBlock conn) {
329         StringBuffer sb = new StringBuffer();
330         if (conn.getSources() != null) {
331             sb.append("[");
332             ModelSource[] srcs = conn.getSources();
333             ModelSource[] srcs2 = new ModelSource[srcs.length];
334             for (int i = 0; i < srcs.length; i++)
335                 srcs2[i] = srcs[i];
336             Arrays.sort(srcs2, keySortComparator);
337             for (int i = 0; i < srcs.length; i++) {
338                 sb.append(srcs[i].getIdentifier());
339                 sb.append(";");
340             }
341             sb.append("]");
342         }
343         sb.append(";");
344         if (conn.getDestination() != null) {
345             sb.append(conn.getDestination().getIdentifier());
346         }
347         sb.append(";");
348         return sb.toString();
349     }
350 
351     private void processSource(ModelSource src, int ix) {
352         ModelIdentifier id = src.getIdentifier();
353         String o = id.getObject();
354         if (o.equals("midi_cc"))
355             processMidiControlSource(src, ix);
356         else if (o.equals("midi_rpn"))
357             processMidiRpnSource(src, ix);
358         else if (o.equals("midi_nrpn"))
359             processMidiNrpnSource(src, ix);
360         else if (o.equals("midi"))
361             processMidiSource(src, ix);
362         else if (o.equals("noteon"))
363             processNoteOnSource(src, ix);
364         else if (o.equals("osc"))
365             return;
366         else if (o.equals("mixer"))
367             return;
368         else
369             ctrl_connections_list.add(ix);
370     }
371 
372     private void processMidiControlSource(ModelSource src, int ix) {
373         String v = src.getIdentifier().getVariable();
374         if (v == null)
375             return;
376         int c = Integer.parseInt(v);
377         if (midi_ctrl_connections[c] == null)
378             midi_ctrl_connections[c] = new int[]{ix};
379         else {
380             int[] olda = midi_ctrl_connections[c];
381             int[] newa = new int[olda.length + 1];
382             for (int i = 0; i < olda.length; i++)
383                 newa[i] = olda[i];
384             newa[newa.length - 1] = ix;
385             midi_ctrl_connections[c] = newa;
386         }
387     }
388 
389     private void processNoteOnSource(ModelSource src, int ix) {
390         String v = src.getIdentifier().getVariable();
391         int c = -1;
392         if (v.equals("on"))
393             c = 3;
394         if (v.equals("keynumber"))
395             c = 4;
396         if (c == -1)
397             return;
398         if (midi_connections[c] == null)
399             midi_connections[c] = new int[]{ix};
400         else {
401             int[] olda = midi_connections[c];
402             int[] newa = new int[olda.length + 1];
403             for (int i = 0; i < olda.length; i++)
404                 newa[i] = olda[i];
405             newa[newa.length - 1] = ix;
406             midi_connections[c] = newa;
407         }
408     }
409 
410     private void processMidiSource(ModelSource src, int ix) {
411         String v = src.getIdentifier().getVariable();
412         int c = -1;
413         if (v.equals("pitch"))
414             c = 0;
415         if (v.equals("channel_pressure"))
416             c = 1;
417         if (v.equals("poly_pressure"))
418             c = 2;
419         if (c == -1)
420             return;
421         if (midi_connections[c] == null)
422             midi_connections[c] = new int[]{ix};
423         else {
424             int[] olda = midi_connections[c];
425             int[] newa = new int[olda.length + 1];
426             for (int i = 0; i < olda.length; i++)
427                 newa[i] = olda[i];
428             newa[newa.length - 1] = ix;
429             midi_connections[c] = newa;
430         }
431     }
432 
433     private void processMidiRpnSource(ModelSource src, int ix) {
434         String v = src.getIdentifier().getVariable();
435         if (v == null)
436             return;
437         int c = Integer.parseInt(v);
438         if (midi_rpn_connections.get(c) == null)
439             midi_rpn_connections.put(c, new int[]{ix});
440         else {
441             int[] olda = midi_rpn_connections.get(c);
442             int[] newa = new int[olda.length + 1];
443             for (int i = 0; i < olda.length; i++)
444                 newa[i] = olda[i];
445             newa[newa.length - 1] = ix;
446             midi_rpn_connections.put(c, newa);
447         }
448     }
449 
450     private void processMidiNrpnSource(ModelSource src, int ix) {
451         String v = src.getIdentifier().getVariable();
452         if (v == null)
453             return;
454         int c = Integer.parseInt(v);
455         if (midi_nrpn_connections.get(c) == null)
456             midi_nrpn_connections.put(c, new int[]{ix});
457         else {
458             int[] olda = midi_nrpn_connections.get(c);
459             int[] newa = new int[olda.length + 1];
460             for (int i = 0; i < olda.length; i++)
461                 newa[i] = olda[i];
462             newa[newa.length - 1] = ix;
463             midi_nrpn_connections.put(c, newa);
464         }
465     }
466 
467     public SoftPerformer(ModelPerformer performer) {
468         this.performer = performer;
469 
470         keyFrom = performer.getKeyFrom();
471         keyTo = performer.getKeyTo();
472         velFrom = performer.getVelFrom();
473         velTo = performer.getVelTo();
474         exclusiveClass = performer.getExclusiveClass();
475         selfNonExclusive = performer.isSelfNonExclusive();
476 
477         Map<String, ModelConnectionBlock> connmap = new HashMap<String, ModelConnectionBlock>();
478 
479         List<ModelConnectionBlock> performer_connections = new ArrayList<ModelConnectionBlock>();
480         performer_connections.addAll(performer.getConnectionBlocks());
481 
482         if (performer.isDefaultConnectionsEnabled()) {
483 
484             // Add modulation depth range (RPN 5) to the modulation wheel (cc#1)
485 
486             boolean isModulationWheelConectionFound = false;
487             for (int j = 0; j < performer_connections.size(); j++) {
488                 ModelConnectionBlock connection = performer_connections.get(j);
489                 ModelSource[] sources = connection.getSources();
490                 ModelDestination dest = connection.getDestination();
491                 boolean isModulationWheelConection = false;
492                 if (dest != null && sources != null && sources.length > 1) {
493                     for (int i = 0; i < sources.length; i++) {
494                         // check if connection block has the source "modulation
495                         // wheel cc#1"
496                         if (sources[i].getIdentifier().getObject().equals(
497                                 "midi_cc")) {
498                             if (sources[i].getIdentifier().getVariable()
499                                     .equals("1")) {
500                                 isModulationWheelConection = true;
501                                 isModulationWheelConectionFound = true;
502                                 break;
503                             }
504                         }
505                     }
506                 }
507                 if (isModulationWheelConection) {
508 
509                     ModelConnectionBlock newconnection = new ModelConnectionBlock();
510                     newconnection.setSources(connection.getSources());
511                     newconnection.setDestination(connection.getDestination());
512                     newconnection.addSource(new ModelSource(
513                             new ModelIdentifier("midi_rpn", "5")));
514                     newconnection.setScale(connection.getScale() * 256.0);
515                     performer_connections.set(j, newconnection);
516                 }
517             }
518 
519             if (!isModulationWheelConectionFound) {
520                 ModelConnectionBlock conn = new ModelConnectionBlock(
521                         new ModelSource(ModelSource.SOURCE_LFO1,
522                         ModelStandardTransform.DIRECTION_MIN2MAX,
523                         ModelStandardTransform.POLARITY_BIPOLAR,
524                         ModelStandardTransform.TRANSFORM_LINEAR),
525                         new ModelSource(new ModelIdentifier("midi_cc", "1", 0),
526                         ModelStandardTransform.DIRECTION_MIN2MAX,
527                         ModelStandardTransform.POLARITY_UNIPOLAR,
528                         ModelStandardTransform.TRANSFORM_LINEAR),
529                         50,
530                         new ModelDestination(ModelDestination.DESTINATION_PITCH));
531                 conn.addSource(new ModelSource(new ModelIdentifier("midi_rpn",
532                         "5")));
533                 conn.setScale(conn.getScale() * 256.0);
534                 performer_connections.add(conn);
535 
536             }
537 
538             // Let Aftertouch to behave just like modulation wheel (cc#1)
539             boolean channel_pressure_set = false;
540             boolean poly_pressure = false;
541             ModelConnectionBlock mod_cc_1_connection = null;
542             int mod_cc_1_connection_src_ix = 0;
543 
544             for (ModelConnectionBlock connection : performer_connections) {
545                 ModelSource[] sources = connection.getSources();
546                 ModelDestination dest = connection.getDestination();
547                 // if(dest != null && sources != null)
548                 if (dest != null && sources != null) {
549                     for (int i = 0; i < sources.length; i++) {
550                         ModelIdentifier srcid = sources[i].getIdentifier();
551                         // check if connection block has the source "modulation
552                         // wheel cc#1"
553                         if (srcid.getObject().equals("midi_cc")) {
554                             if (srcid.getVariable().equals("1")) {
555                                 mod_cc_1_connection = connection;
556                                 mod_cc_1_connection_src_ix = i;
557                             }
558                         }
559                         // check if channel or poly pressure are already
560                         // connected
561                         if (srcid.getObject().equals("midi")) {
562                             if (srcid.getVariable().equals("channel_pressure"))
563                                 channel_pressure_set = true;
564                             if (srcid.getVariable().equals("poly_pressure"))
565                                 poly_pressure = true;
566                         }
567                     }
568                 }
569 
570             }
571 
572             if (mod_cc_1_connection != null) {
573                 if (!channel_pressure_set) {
574                     ModelConnectionBlock mc = new ModelConnectionBlock();
575                     mc.setDestination(mod_cc_1_connection.getDestination());
576                     mc.setScale(mod_cc_1_connection.getScale());
577                     ModelSource[] src_list = mod_cc_1_connection.getSources();
578                     ModelSource[] src_list_new = new ModelSource[src_list.length];
579                     for (int i = 0; i < src_list_new.length; i++)
580                         src_list_new[i] = src_list[i];
581                     src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
582                             new ModelIdentifier("midi", "channel_pressure"));
583                     mc.setSources(src_list_new);
584                     connmap.put(extractKeys(mc), mc);
585                 }
586                 if (!poly_pressure) {
587                     ModelConnectionBlock mc = new ModelConnectionBlock();
588                     mc.setDestination(mod_cc_1_connection.getDestination());
589                     mc.setScale(mod_cc_1_connection.getScale());
590                     ModelSource[] src_list = mod_cc_1_connection.getSources();
591                     ModelSource[] src_list_new = new ModelSource[src_list.length];
592                     for (int i = 0; i < src_list_new.length; i++)
593                         src_list_new[i] = src_list[i];
594                     src_list_new[mod_cc_1_connection_src_ix] = new ModelSource(
595                             new ModelIdentifier("midi", "poly_pressure"));
596                     mc.setSources(src_list_new);
597                     connmap.put(extractKeys(mc), mc);
598                 }
599             }
600 
601             // Enable Vibration Sound Controllers : 76, 77, 78
602             ModelConnectionBlock found_vib_connection = null;
603             for (ModelConnectionBlock connection : performer_connections) {
604                 ModelSource[] sources = connection.getSources();
605                 if (sources.length != 0
606                         && sources[0].getIdentifier().getObject().equals("lfo")) {
607                     if (connection.getDestination().getIdentifier().equals(
608                             ModelDestination.DESTINATION_PITCH)) {
609                         if (found_vib_connection == null)
610                             found_vib_connection = connection;
611                         else {
612                             if (found_vib_connection.getSources().length > sources.length)
613                                 found_vib_connection = connection;
614                             else if (found_vib_connection.getSources()[0]
615                                     .getIdentifier().getInstance() < 1) {
616                                 if (found_vib_connection.getSources()[0]
617                                         .getIdentifier().getInstance() >
618                                         sources[0].getIdentifier().getInstance()) {
619                                     found_vib_connection = connection;
620                                 }
621                             }
622                         }
623 
624                     }
625                 }
626             }
627 
628             int instance = 1;
629 
630             if (found_vib_connection != null) {
631                 instance = found_vib_connection.getSources()[0].getIdentifier()
632                         .getInstance();
633             }
634             ModelConnectionBlock connection;
635 
636             connection = new ModelConnectionBlock(
637                 new ModelSource(new ModelIdentifier("midi_cc", "78"),
638                     ModelStandardTransform.DIRECTION_MIN2MAX,
639                     ModelStandardTransform.POLARITY_BIPOLAR,
640                     ModelStandardTransform.TRANSFORM_LINEAR),
641                 2000, new ModelDestination(
642                     new ModelIdentifier("lfo", "delay2", instance)));
643             connmap.put(extractKeys(connection), connection);
644 
645             final double scale = found_vib_connection == null ? 0
646                     : found_vib_connection.getScale();
647             connection = new ModelConnectionBlock(
648                 new ModelSource(new ModelIdentifier("lfo", instance)),
649                 new ModelSource(new ModelIdentifier("midi_cc", "77"),
650                     new ModelTransform() {
651                         double s = scale;
652                         public double transform(double value) {
653                             value = value * 2 - 1;
654                             value *= 600;
655                             if (s == 0) {
656                                 return value;
657                             } else if (s > 0) {
658                                 if (value < -s)
659                                     value = -s;
660                                 return value;
661                             } else {
662                                 if (value < s)
663                                     value = -s;
664                                 return -value;
665                             }
666                         }
667                     }), new ModelDestination(ModelDestination.DESTINATION_PITCH));
668             connmap.put(extractKeys(connection), connection);
669 
670             connection = new ModelConnectionBlock(
671                 new ModelSource(new ModelIdentifier("midi_cc", "76"),
672                     ModelStandardTransform.DIRECTION_MIN2MAX,
673                     ModelStandardTransform.POLARITY_BIPOLAR,
674                     ModelStandardTransform.TRANSFORM_LINEAR),
675                 2400, new ModelDestination(
676                     new ModelIdentifier("lfo", "freq", instance)));
677             connmap.put(extractKeys(connection), connection);
678 
679         }
680 
681         // Add default connection blocks
682         if (performer.isDefaultConnectionsEnabled())
683             for (ModelConnectionBlock connection : defaultconnections)
684                 connmap.put(extractKeys(connection), connection);
685         // Add connection blocks from modelperformer
686         for (ModelConnectionBlock connection : performer_connections)
687             connmap.put(extractKeys(connection), connection);
688         // seperate connection blocks : Init time, Midi Time, Midi/Control Time,
689         // Control Time
690         List<ModelConnectionBlock> connections = new ArrayList<ModelConnectionBlock>();
691 
692         midi_ctrl_connections = new int[128][];
693         for (int i = 0; i < midi_ctrl_connections.length; i++) {
694             midi_ctrl_connections[i] = null;
695         }
696         midi_connections = new int[5][];
697         for (int i = 0; i < midi_connections.length; i++) {
698             midi_connections[i] = null;
699         }
700 
701         int ix = 0;
702         boolean mustBeOnTop = false;
703 
704         for (ModelConnectionBlock connection : connmap.values()) {
705             if (connection.getDestination() != null) {
706                 ModelDestination dest = connection.getDestination();
707                 ModelIdentifier id = dest.getIdentifier();
708                 if (id.getObject().equals("noteon")) {
709                     mustBeOnTop = true;
710                     if (id.getVariable().equals("keynumber"))
711                         forcedKeynumber = true;
712                     if (id.getVariable().equals("velocity"))
713                         forcedVelocity = true;
714                 }
715             }
716             if (mustBeOnTop) {
717                 connections.add(0, connection);
718                 mustBeOnTop = false;
719             } else
720                 connections.add(connection);
721         }
722 
723         for (ModelConnectionBlock connection : connections) {
724             if (connection.getSources() != null) {
725                 ModelSource[] srcs = connection.getSources();
726                 for (int i = 0; i < srcs.length; i++) {
727                     processSource(srcs[i], ix);
728                 }
729             }
730             ix++;
731         }
732 
733         this.connections = new ModelConnectionBlock[connections.size()];
734         connections.toArray(this.connections);
735 
736         this.ctrl_connections = new int[ctrl_connections_list.size()];
737 
738         for (int i = 0; i < this.ctrl_connections.length; i++)
739             this.ctrl_connections[i] = ctrl_connections_list.get(i);
740 
741         oscillators = new ModelOscillator[performer.getOscillators().size()];
742         performer.getOscillators().toArray(oscillators);
743 
744         for (ModelConnectionBlock conn : connections) {
745             if (conn.getDestination() != null) {
746                 if (isUnnecessaryTransform(conn.getDestination().getTransform())) {
747                     conn.getDestination().setTransform(null);
748                 }
749             }
750             if (conn.getSources() != null) {
751                 for (ModelSource src : conn.getSources()) {
752                     if (isUnnecessaryTransform(src.getTransform())) {
753                         src.setTransform(null);
754                     }
755                 }
756             }
757         }
758 
759     }
760 
761     private static boolean isUnnecessaryTransform(ModelTransform transform) {
762         if (transform == null)
763             return false;
764         if (!(transform instanceof ModelStandardTransform))
765             return false;
766         ModelStandardTransform stransform = (ModelStandardTransform)transform;
767         if (stransform.getDirection() != ModelStandardTransform.DIRECTION_MIN2MAX)
768             return false;
769         if (stransform.getPolarity() != ModelStandardTransform.POLARITY_UNIPOLAR)
770             return false;
771         if (stransform.getTransform() != ModelStandardTransform.TRANSFORM_LINEAR)
772             return false;
773         return false;
774     }
775 }